/*
 * ConfigurationWriter.java
 *
 * Created on August 21, 2006, 9:14 PM
 *
 * @author Francois Daniels
 */

package za.gateway.io;

import za.gateway.container.*;
import za.gateway.backend.*;
import za.gateway.preferences.*;
import java.io.*;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;

/**
 * This class is responsible for creating the configuration files. It 
 * does this via the static functions provided.
 *
 * @author Francois Daniels
 */
public abstract class ConfigurationWriter {
    /**
     * This function will create an dial plan for Asterisk based on the 
     * parameters' values.
     *
     * @param Elements          This is a list of all the 
     *                          AccessControlElements in the system.
     * @param RestrictionData   This is the list of restrictions found 
     *                          on the system.
     * @param SoftData          This is the list of the software phones 
     *                          connected to the system.
     * @param HardData          This is the list of hardware (fxs) 
     *                          phones connected to the system.
     * @param Filename          This is the name of the file in which 
     *                          the output will be stored.
     * @return                  true if the operation succeeds, false 
     *                          otherwise.
     */
    public static boolean CreateExtensionsFile (
            AccessControlElement [] Elements, 
            Restrictions RestrictionData, SoftPhone [] SoftData, 
            Phone [] HardData, String Filename) {
        
        Hashtable Table = new Hashtable ();
        Vector PhonesList = new Vector ();        
        
        // If either are these are null then we can't continue
        if (Elements == null || RestrictionData == null) {
            return false;
        }
        
        // For each element
        for (int i = 0; i < Elements.length; i++) {
            String GroupName = Elements [i].GetGroupName ();
            String PhoneNum  = Elements [i].GetPhoneNumber ();
            
            Restriction Restrict = RestrictionData.GetData (
                    Elements [i].GetRestrictionName ());            
            
            if (Restrict == null) {
                continue;
            }
            
            // We get this restriction's data
            int StartTime = Restrict.GetStartTime ();
            int EndTime = Restrict.GetEndTime ();
            int Type = Restrict.GetType ();
            String StartDay = Restrict.GetStartDay ();
            String EndDay = Restrict.GetEndDay ();
            String Forward = PhoneNum;
            
            if (Type == Restrict.FORWARD)
                Forward = Restrict.GetExtra ();
            
            ExtensionElement Element = 
                    (ExtensionElement) Table.get (GroupName);
            
            if (Element == null)
                Element = new ExtensionElement ();
            
            // We this add this restriction rule to the extension list
            Element.add (PhoneNum, StartTime, EndTime, Type, Forward, 
                    StartDay, EndDay);
            
            // We then store it in our hashtable
            Table.put (GroupName, Element);
        }
        
        // If we have software phones on the system, the we add it to
        // our list
        if (SoftData != null) {
            for (int i = 0; i < SoftData.length; i++) {
                String Number = SoftData [i].GetPhoneNumber();
                String Dest = "SIP/"+SoftData [i].GetNameData();
                PhonesList.add (new DialData (Number, Dest));
            }
        }
        
        // We do the same for hardware phones
        if (HardData != null) {
            for (int i = 0; i < HardData.length; i++) {
                String Number = HardData [i].GetPhoneNumber();
                String Dest = HardData [i].GetPort();
                PhonesList.add (new DialData (Number, Dest));
            }
        }               
        
        // We then write the extensions configuration file
        WriteExtensionsFile (Table, PhonesList, Filename);
        return true;
    }
    
    /**
     * This class is responsible for storing details of how Asterisk 
     * accesses each phone. That is the dial instructions Asterisk uses 
     * to access the phone. Each phone has a phone number and a 
     * destination. The phone number is the keys a user has to hit to 
     * call that phone. The Destination is the interface Asterisk uses 
     * to access this phone. For SIP phones it will be SIP/[username] 
     * and for FXS phones it will be Zap/[number], where number is the 
     * port number.
     */
    private static class DialData {
        /**
         * The phone number is the keys a user has to hit to call that 
         * phone.
         */
        public String PhoneNumber = new String ();
        
        /**
         * The Destination is the interface Asterisk uses to access 
         * this phone. i.e. Zap/[number] or SIP/[username].
         */
        public String Destination = new String ();
        
        /**
         * Creates a new DialData object.
         *
         * @param PhoneNumber   This is the phone number we are 
         *                      interested in.
         * @param Destination   This is the way Asterisk gets there. 
         *                      For hardware phones it will usually be 
         *                      Zap/[number], whereas for software 
         *                      phones it will be SIP/[username].
         */
        public DialData (String PhoneNumber, String Destination) {
            this.PhoneNumber = PhoneNumber;
            this.Destination = Destination;
        }
    }
    
    /**
     * This private function will be responsible for creating sensible 
     * dial plans. What this means is that the dial plans will be 
     * written such that more restrictive rules will appear above less 
     * restrictive ones. This holds true for FORWARD vs ALLOW rules.
     *
     * @param Table         A list of elements which contains the items 
     *                      that we are going to write to the dial 
     *                      plan.
     * @param PhonesList    This is a list of all the phones on the 
     *                      system.
     * @param Filename      This is the name of the output file.
     */
    protected static void WriteExtensionsFile (Hashtable Table, Vector 
            PhonesList, String Filename) {
        
        if (Table == null) {
            return;
        }
        BufferedWriter File = null;
        
        try {
            File = new BufferedWriter (new FileWriter (Filename));
        } catch (IOException e) {
            Log.ERROR(e);
        }
        
        try {
            
            // We write the system_dial context. This is where we place
            // all calls from
            File.write ("[system_dial]\n");
            for (int i = 0; i < PhonesList.size(); i++) {
                DialData Data = (DialData) PhonesList.elementAt(i);
                String Output = "exten => " + Data.PhoneNumber +
                        ",1,Dial(" + Data.Destination + ")\n";                
                Output += "exten => "+ Data.PhoneNumber + 
                        ",2,Hangup()\n";
                File.write(Output);                        
            }
            
            // We add the default rule for numbers which have not been
            // configured on the system
            String Output = "exten => _X.,1,Playback(" + 
                    Settings.invalidNumber+")\nexten => _X.,2," +
                    "Hangup()\n\n";            
            File.write(Output);
        } catch (IOException e) {
            Log.ERROR (e);
        }
        
        for (Enumeration Keys = Table.keys (); 
            Keys.hasMoreElements ();) {
            
            String Key = Keys.nextElement ().toString ();
            
            try {
                
                // We write each context to our extensions file
                File.write ("["+Key+"]\n");
                
                ExtensionElement Element = 
                        (ExtensionElement) Table.get (Key);
                File.write (Element.toString ());
                File.write ("\n");
            } catch (Exception e) {
                Log.ERROR (e);
            }
        }
        
        try {
            if (File != null)
                File.close ();
        } catch (Exception e) {}
    }
    
    /**
     * This function will create a SIP configuration file for Asterisk.
     * These files are used to determine which software phones are 
     * allowed to connect to the system, as well as setting their 
     * usernames and passwords.
     *
     * @param Phones    This is the list of software phones that have
     *                  access to the system.
     * @param Filename  This is the name of the output file.
     * @return          returns true if this operation succeeds, false 
     *                  otherwise.
     */
    public static boolean CreateSIPFile (SoftPhone [] Phones, 
            String Filename) {
        
        if (Phones == null)
            return false;
        BufferedWriter File;
        
        try {
            File = new BufferedWriter (new FileWriter (Filename));
        } catch (IOException e) {
            return false;
        }
        
        for (int i = 0; i < Phones.length; i++) {
            String Username = Phones [i].GetUsername ();
            String Password = Phones [i].GetPassword ();
            String PhoneNum = Phones [i].GetPhoneNumber ();            
            Username        = NormaliseString (Username);
            
            // We write the configuration for this softphone
            String SIPConfiguration =   "["+Username+"]\n"+
                    "username="+Username+"\n"+
                    "secret="+Password+"\n"+
                    "callerid=\""+PhoneNum+"\"\n"+
                    "context=Incomming\n"+
                    "type=friend\n"+
                    "host=dynamic\n"+
                    "nat=yes\n"+
                    "canreinvite=no\n"+
                    "disallow=all\n"+
                    "allow=gsm\n"+
                    "allow=ulaw\n"+
                    "allow=alaw\n\n";
            try {
            File.write (SIPConfiguration);
            } catch (IOException e) {
                Log.ERROR(e);
            }
        }
        
        try {
                File.close ();
            } catch (IOException e) {
                return false;
            }
        
        return true;
    }
    
    /**
     * This function removes all alpha characters from a string.
     *
     * @param Input     The string from which we would like to remove 
     *                  non alpha characters.
     * @return          The string which has had all non alpha 
     *                  characters from it.
     */
    private static String NormaliseString (String Input) {
        if (Input == null) return null;
        String Output = new String ();
        
        // For each character in the string
        for (int i = 0; i < Input.length (); i++) {
            
            // We only add the character if it is an alpha character
            if (Character.isLetter (Input.charAt (i)))
                Output += Input.charAt (i);
        }
        
        // Return the new string
        return Output;
    }
}
